#ifndef __SOLUTION_HPP__
#define __SOLUTION_HPP__

#include "globaldef.hpp"
#include "Map.hpp"
#include "Berth.hpp"
#include "Boat.hpp"
#include "BoatDispatcher.hpp"
#include "Robot.hpp"
#include "RobotDispatcher.hpp"
#include "Goods.hpp"
#include "Planner.hpp"
#include "PlannerRandom.hpp"
#include "PlannerBindRobot.hpp"
#include "PlannerPreQue.hpp"
#include "PlannerWorkGroup.hpp"
#include <cstdio>
#include <vector>
#include <cmath>

class Solution {
public:
    Map map; // 地图
    std::vector<Berth> berths; // 泊位
    std::vector<Boat> boats; // 船
    std::vector<BoatDispatcher> boatDispatchers; // 船调度器
    std::vector<Robot> robots; // 机器人
    std::vector<RobotDispatcher> robotDispatchers; // 机器人调度器
    std::vector<Goods *> goods_list; // 货物
    Planner *planner = nullptr; // 规划器

public:
    /**
     * @brief 构造函数
     */
    Solution():
        berths(BERTH_NUM),
        boats(BOAT_NUM),
        boatDispatchers(BOAT_NUM, BoatDispatcher(map)),
        robots(ROBOT_NUM),
        robotDispatchers(ROBOT_NUM, RobotDispatcher(map)) {
            for (int id = 0; id < BOAT_NUM; ++id) {
                boatDispatchers[id].attach(&boats[id]);
            }
            for (int id = 0; id < ROBOT_NUM; ++id) {
                robotDispatchers[id].attach(&robots[id]);
            }
    }
    /**
     * @brief 析构函数
     */
    ~Solution() {
        #ifdef USE_LOG
        int volume = boats[0].volume; // 船容积
        int allVolume = BoatDispatcher::sellCnt * volume; // 运量空间
        LOG("发船次数[%d] 运量空间[%d] 使用率[%.2f%%]\n", BoatDispatcher::sellCnt, allVolume,
            BoatDispatcher::sellGoodsCnt / static_cast<float>(allVolume) * 100.0);
        LOG("=======================================\n");
        bool isRemain = false;
        for (auto const &boat : boats) {
            if (boat.getGoodsNum() > 0) { // 有货物再输出
                LOG("轮船[%d] 剩余货物[%3d/%03u]=[% 6d]\n", boat.id, boat.getGoodsNum(), boat.volume, boat.getValue());
                isRemain = true;
            }
        }
        if (!isRemain) {
            LOG("所有船均成功完成卸货！\n")
        }
        LOG("=======================================\n");
        int remainCnt = 0;
        int remainValue = 0;
        for (auto const &berth : berths) {
            int cnt = berth.getGoodsNum();
            if (cnt > 0) { // 有货物再输出
                remainCnt += cnt;
                int value = berth.getValue();
                remainValue += value;
                LOG("泊位[%d] 剩余货物[%3d/%03d]=[%6d]\n", berth.id, cnt, volume, value);
            }
        }
        if (remainCnt > 0) {
            LOG("---------------------------------------\n");
            LOG("泊位剩余货物 总量[%3d/%03d]=[%6d]\n", remainCnt, volume, remainValue);
        } else {
            LOG("所有泊位均无货物剩余！\n")
        }
        #endif
        for (auto goods : goods_list) {
            delete goods;
        }
        #ifdef USE_LOG
        if (Goods::amount > 0) {
            LOG("内存泄漏: \"Goods\" object: [%d]!\n", Goods::amount);
        }
        #endif
        if (planner) {
            delete planner;
        }
        LOG("=======================================\n");
    }
    /**
     * @brief 处理初始化信息
     */
    void init() {
        // 设置泊位装载时间
        for (auto &berth : berths) {
            berth.setLoadTime(boats[0].volume);
        }

        // 判断地图编号
        map.init();
        PARAMS.init(map.id); // 选择相应的参数表

        // 锁定指定泊位
        for (auto berth : PARAMS.LOCK_BERTHS) {
            berths[berth].lock();
        }

        // 计算地图信息
        for (int i = 0; i < BERTH_NUM; ++i) {
            if (berths[i].isLocked()) {
                continue;
            }
            const Berth &berth = berths[i];
            map.initBerthDistance(berth.id, berth);
        }
        map.initNearestBerthTable();

        // 选择规划器
        switch(PARAMS.Planner) {
        case Hyperparameters::Random:
            this->planner = new PlannerRandom();
            break;
        case Hyperparameters::BindRobot:
            this->planner = new PlannerBindRobot();
            break;
        case Hyperparameters::PreQue:
            this->planner = new PlannerPreQue();
            break;
        default:
        case Hyperparameters::WorkGroup:
            this->planner = new PlannerWorkGroup();
            break;
        }
        this->planner->init(&map, &berths, &goods_list, &boatDispatchers, &robotDispatchers);

        // 如果机器人到不了任何港口，将其调度器锁定
        for (int i = 0; i < ROBOT_NUM; ++i) {
            if (!map.isBerthArrival(map.robotsInit[i])) {
                robotDispatchers[i].lock();
                LOG("锁定机器人[%d]!\n", i);
            }
        }

        #ifdef USE_LOG
        for (auto berth : berths) {
            if (!berth.locked) {
                LOG("泊位[%1d] 面积[%4d] 装满时间[%2d] 距离[%4d]\n", berth.id, map.getBerthAreaS(berth.id), berth.loadTime, berth.distance);
            }
        }
        #endif
    }
    /**
     * @brief 读入判题器返回的OK
     */
    void scanOK() {
        char okk[100];
        scanf("%2s", okk);
    }
    /**
     * @brief 输出OK响应判题器
     */
    void printOK() {
        std::puts("OK");
        std::fflush(stdout); // 标准输出需要及时刷新
    }
    /**
     * @brief 给空闲机器人或船分配任务
     */
    void runPlanner() {
        for (auto &it : robotDispatchers) {
            if (it.isFree()) {
                it.setPlan(planner->allocatePlan(it.robot));
            }
        }
        for (auto &it : boatDispatchers) {
            if (it.isFree()) {
                it.setPlan(planner->allocatePlan(it.boat));
            }
        }
    }
    /**
     * @brief 运行所有调度器
     * @param frameID
     */
    void runDispatchers(int frameID) {
        // 机器人调度器
        for (auto &it : robotDispatchers) {
            if (!it.isLock && it.robot->carried == false) { // 优先调度取货路上的机器人
                it.run(frameID);
            }
        }
        for (auto &it : robotDispatchers) {
            if (!it.isLock && it.robot->carried == true) {
                it.run(frameID);
            }
        }

        // 船调度器
        for (auto &it : boatDispatchers) {
            if (!it.isLock) {
                it.run(frameID);
            }
        }
    }
    /**
     * @brief 运行一帧
     * @param frameID
     * @param newGoodsNum
     */
    void run(int frameID, int newGoodsNum) {
        // 刷新地图
        map.refresh();

        // 新货物计算距离表
        int cnt = 0;
        for (auto it = goods_list.rbegin(); it != goods_list.rend(); ++it) {
            if (cnt++ >= newGoodsNum) {
                break;
            }
            (*it)->distance = map.getDistanceTable(**it);
        }

        // 刷新规划器信息
        planner->refresh(frameID, newGoodsNum);

        // 处理机器人到泊位的货物转移
        for (auto &it : robotDispatchers) {
            if (it.isFree()) {
                // 调度器空闲时，说明卸货完成，将货物挂载到泊位的货物表中
                if (it.plan.goods != nullptr && it.plan.berthID != INVALID_BERTH_ID) {
                    berths[it.plan.berthID].goods_list.push_back(it.plan.goods);
                    it.plan.goods->setUnused(); // 不再需要距离表
                    it.plan.goods = nullptr;
                    it.plan.berthID = INVALID_BERTH_ID;
                }
            }
        }

        // 给空闲机器人分配任务
        runPlanner();

        // 调度器工作
        runDispatchers(frameID);

        // 处理泊位到船的货物转移
        for (auto &boat : boats) {
            if (boat.state == BOAT_LOADING) {
                if (boat.berthID == VIRTUAL_BERTH_ID) { // 在虚拟点瞬间完成卸货
                    while (!boat.goods_list.empty()) {
                        boat.goods_list.pop_front();
                    }
                } else {
                    auto &berth = berths[boat.berthID];
                    for (int i = 0; i < berth.velocity; ++i) {
                        if (berth.goods_list.empty() || boat.isFull()) {
                            break;
                        }
                        auto goods = berth.goods_list.front();
                        berth.goods_list.pop_front();
                        boat.goods_list.push_back(goods);
                    }
                }
            }
        }
    }
};

#endif // __SOLUTION_HPP__